home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- smtp.c
-
- This reusable module implements SMTP mail.
-
- The following functions are exported:
-
- SmtpOpen - Open an SMTP stream.
- SmtpClose - Close an SMTP stream.
- SmtpSendMessage - Send a mail message.
-
- The following functions are used to get error information:
-
- SmtpGetServerErrInfo - Get server error information.
- SmtpGetOSErr - Get OS error number.
-
- The NetInit function in module net.c must be called before calling any of
- the functions in this module. You also must call the NetIdle function in your
- idle loop, and the NetTerm function at program termination.
-
- A "stream" is an abstraction representing a bidirectional network connection
- to an SMTP server. A stream is represented as a handle of type "SmtpStreamHandle".
- These stream handles are opaque. You may copy them and pass them as parameters
- to functions in smtp.c, but you are prohibited from accessing the contents of
- the memory blocks pointed to by the stream handles. Only the functions in
- smtp.c are permitted to manipulate the contents of these blocks.
-
- The functions return a value of type SmtpErr as the function result:
-
- smtpNoErr no error occurred
- smtpServerErr server error
- smtpOSErr OS error
-
- If the function result is smtpServerErr, the SmtpGetServerErrInfo function should
- be called to get information about the server error. On server errors, the
- stream is still open on return to the caller (except in SmtpOpen, which creates the
- stream only if no errors of any kind occur).
-
- If the function result is smtpOSErr, the SmtpGetOSErr function should be called
- to get the OS error number:
-
- userCanceledErr if the user canceled the operation
- other any other OS, Toolbox, or MacTCP error code
-
- If the user cancels an operation or any other OS error occurs, the stream
- is aborted and deallocated before returning to the caller. "Aborted" means that
- the server connection is closed abruptly, without going through the usual TCP
- stream teardown process. You must perform careful error checking. The stream is
- deallocated, and must not be reused.
-
- Copyright © 1994, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
-
- #include "def.h"
- #include "smtp.h"
- #include "net.h"
- #include "memutil.h"
-
-
-
- /* Types. */
-
- typedef struct TStream {
- NetStreamHandle netStream; /* net stream handle */
- Boolean needReset; /* true if must send RSET command before
- next message */
- } TStream, *TStreamPtr, **TStreamHandle;
-
-
-
- /* Global variables. */
-
- static OSErr gErr = noErr; /* OS error code */
- static CStr255 gCommand = ""; /* server command */
- static long gResponseCode = 0; /* server response code */
- static CStr255 gResponse = ""; /* server response message */
-
-
-
- /*----------------------------------------------------------------------------
- SmtpOpen
-
- Open an SMTP stream.
-
- Entry: host = server host address (domain name or dotted
- decimal IP address).
-
- Exit: function result = result code.
- stream = handle to opened stream,
- or nil if function result != smtpNoErr.
- ----------------------------------------------------------------------------*/
-
- SmtpErr SmtpOpen (char *host, SmtpStreamHandle *stream)
- {
- TStreamHandle s;
- unsigned long addr;
- unsigned short port;
- NetStreamHandle netStream;
- CStr255 myName;
-
- *stream = nil;
- *gCommand = 0;
-
- gErr = NetNameToAddr(host, kSMTPPort, &addr, &port);
- if (gErr != noErr) return smtpOSErr;
-
- gErr = NetOpen(addr, port, &netStream, &gResponseCode, gResponse);
- if (gErr != noErr) return smtpOSErr;
- if (gResponseCode != 220) goto exit1;
-
- gErr = NetGetMyName(myName);
- if (gErr != noErr) {
- if (gErr == userCanceledErr) goto exit2;
- gErr = NetGetMyAddrStr(myName);
- if (gErr != noErr) goto exit2;
- }
- sprintf(gCommand, "HELO %.250s", myName);
- gErr = NetCommand(netStream, gCommand, &gResponseCode, gResponse);
- if (gErr != noErr) return smtpOSErr;
- if (gResponseCode != 250) goto exit1;
-
- gErr = MyNewHandle(sizeof(TStream), &s);
- if (gErr != noErr) goto exit2;
-
- (**s).netStream = netStream;
- (**s).needReset = false;
-
- *stream = (SmtpStreamHandle)s;
- return smtpNoErr;
-
- exit1:
-
- NetClose(netStream);
- return smtpServerErr;
-
- exit2:
-
- NetClose(netStream);
- return smtpOSErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SmtpClose
-
- Close an SMTP stream.
-
- Entry: stream = handle to stream.
-
- Exit: function result = result code (always smtpNoErr).
- ----------------------------------------------------------------------------*/
-
- SmtpErr SmtpClose (SmtpStreamHandle stream)
- {
- TStreamHandle s;
-
- s = (TStreamHandle)stream;
- NetClose((**s).netStream);
- MyDisposeHandle(s);
- return smtpNoErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SmtpSendMessage
-
- Send a mail message.
-
- Entry: stream = handle to stream.
- from = C-format email address of sender.
- to = C-format email address of recipient.
- text = handle to message text, including SMTP header, with CR line
- terminators. Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
-
- Exit: function result = result code.
-
- Multiple recipients may be listed in the "to" string, separated by commas.
- ----------------------------------------------------------------------------*/
-
- SmtpErr SmtpSendMessage (SmtpStreamHandle stream, char *from, char *to, Handle text)
- {
- TStreamHandle s;
- NetStreamHandle netStream;
- char *p, *q;
- short len;
-
- s = (TStreamHandle)stream;
- netStream = (**s).netStream;
-
- if ((**s).needReset) {
- strcpy(gCommand, "RSET");
- gErr = NetCommand(netStream, gCommand, &gResponseCode, gResponse);
- if (gErr != noErr) goto exit1;
- if (gResponseCode != 250) goto exit2;
- (**s).needReset = false;
- }
-
- sprintf(gCommand, "MAIL FROM:<%.243s>", from);
- gErr = NetCommand(netStream, gCommand, &gResponseCode, gResponse);
- if (gErr != noErr) goto exit1;
- if (gResponseCode != 250) goto exit2;
-
- p = to;
- while (*p != 0) {
- q = p;
- while (*q != 0 && *q != ',') q++;
- len = q-p;
- if (len > 0 && len <= 245) {
- sprintf(gCommand, "RCPT TO:<%.*s>", len, p);
- gErr = NetCommand(netStream, gCommand, &gResponseCode, gResponse);
- if (gErr != noErr) goto exit1;
- if (gResponseCode != 250 && gResponseCode != 251) goto exit2;
- }
- p = q;
- if (*p == ',') p++;
- }
-
- strcpy(gCommand, "DATA");
- gErr = NetCommand(netStream, gCommand, &gResponseCode, gResponse);
- if (gErr != noErr) goto exit1;
- if (gResponseCode != 354) goto exit2;
-
- gErr = NetSendText(netStream, text);
- if (gErr != noErr) goto exit1;
-
- gErr = NetGetExtraResponse(netStream, &gResponseCode, gResponse);
- if (gErr != noErr) goto exit1;
- if (gResponseCode != 250 && gResponseCode != 251) goto exit2;
-
- return smtpNoErr;
-
- exit1:
-
- MyDisposeHandle(s);
- return smtpOSErr;
-
- exit2:
-
- (**s).needReset = true;
- return smtpServerErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SmtpGetServerErrInfo
-
- Get server error information.
-
- Exit: cmd = C-format server command.
- num = server error number.
- msg = C-format server error message.
- ----------------------------------------------------------------------------*/
-
- void SmtpGetServerErrInfo (CStr255 cmd, long *num, CStr255 msg)
- {
- strcpy(cmd, gCommand);
- *num = gResponseCode;
- strcpy(msg, gResponse);
- }
-
-
-
- /*----------------------------------------------------------------------------
- SmtpGetOSErr
-
- Get the OS error code.
-
- Exit: function result = OS error code.
- ----------------------------------------------------------------------------*/
-
- OSErr SmtpGetOSErr (void)
- {
- return gErr;
- }
-